#include <xen/stringify.h>
#include <xen/vga.h>
#include <asm/e820.h>
+#include <asm/mm.h>
#include <asm/msr.h>
#include <asm/processor.h>
for( ; ; ); /* not reached */
}
+static __init void copy_mapping(unsigned long mfn, unsigned long end,
+ bool_t (*is_valid)(unsigned long smfn,
+ unsigned long emfn))
+{
+ unsigned long next;
+
+ for ( ; mfn < end; mfn = next )
+ {
+ l4_pgentry_t l4e = efi_l4_pgtable[l4_table_offset(mfn << PAGE_SHIFT)];
+ l3_pgentry_t *l3src, *l3dst;
+ unsigned long va = (unsigned long)mfn_to_virt(mfn);
+
+ next = mfn + (1UL << (L3_PAGETABLE_SHIFT - PAGE_SHIFT));
+ if ( !is_valid(mfn, min(next, end)) )
+ continue;
+ if ( !(l4e_get_flags(l4e) & _PAGE_PRESENT) )
+ {
+ l3dst = alloc_xen_pagetable();
+ BUG_ON(!l3dst);
+ clear_page(l3dst);
+ efi_l4_pgtable[l4_table_offset(mfn << PAGE_SHIFT)] =
+ l4e_from_paddr(virt_to_maddr(l3dst), __PAGE_HYPERVISOR);
+ }
+ else
+ l3dst = l4e_to_l3e(l4e);
+ l3src = l4e_to_l3e(idle_pg_table[l4_table_offset(va)]);
+ l3dst[l3_table_offset(mfn << PAGE_SHIFT)] = l3src[l3_table_offset(va)];
+ }
+}
+
+static bool_t __init ram_range_valid(unsigned long smfn, unsigned long emfn)
+{
+ unsigned long sz = pfn_to_pdx(emfn - 1) / PDX_GROUP_COUNT + 1;
+
+ return !(smfn & pfn_hole_mask) &&
+ find_next_bit(pdx_group_valid, sz,
+ pfn_to_pdx(smfn) / PDX_GROUP_COUNT) < sz;
+}
+
+static bool_t __init rt_range_valid(unsigned long smfn, unsigned long emfn)
+{
+ return 1;
+}
+
+#define INVALID_VIRTUAL_ADDRESS (0xBAAADUL << \
+ (EFI_PAGE_SHIFT + BITS_PER_LONG - 32))
+
void __init efi_init_memory(void)
{
unsigned int i;
if ( !(desc->Attribute & EFI_MEMORY_RUNTIME) )
continue;
+ desc->VirtualStart = INVALID_VIRTUAL_ADDRESS;
+
smfn = PFN_DOWN(desc->PhysicalStart);
emfn = PFN_UP(desc->PhysicalStart + len);
- desc->VirtualStart = 0xBAAADUL << (EFI_PAGE_SHIFT + BITS_PER_LONG - 32);
-
if ( desc->Attribute & EFI_MEMORY_WB )
/* nothing */;
else if ( desc->Attribute & EFI_MEMORY_WT )
#if 0 /* Incompatible with kexec. */
efi_rs->SetVirtualAddressMap(efi_memmap_size, efi_mdesc_size,
mdesc_ver, efi_memmap);
+#else
+ /* Set up 1:1 page tables to do runtime calls in "physical" mode. */
+ efi_l4_pgtable = alloc_xen_pagetable();
+ BUG_ON(!efi_l4_pgtable);
+ clear_page(efi_l4_pgtable);
+
+ copy_mapping(0, max_page, ram_range_valid);
+
+ /* Insert non-RAM runtime mappings. */
+ for ( i = 0; i < efi_memmap_size; i += efi_mdesc_size )
+ {
+ const EFI_MEMORY_DESCRIPTOR *desc = efi_memmap + i;
+
+ if ( desc->Attribute & EFI_MEMORY_RUNTIME )
+ {
+ if ( desc->VirtualStart != INVALID_VIRTUAL_ADDRESS )
+ copy_mapping(PFN_DOWN(desc->PhysicalStart),
+ PFN_UP(desc->PhysicalStart +
+ (desc->NumberOfPages << EFI_PAGE_SHIFT)),
+ rt_range_valid);
+ else
+ /* XXX */;
+ }
+ }
+
+ /* Insert Xen mappings. */
+ for ( i = l4_table_offset(HYPERVISOR_VIRT_START);
+ i < l4_table_offset(HYPERVISOR_VIRT_END); ++i )
+ efi_l4_pgtable[i] = idle_pg_table[i];
#endif
}
#define efi_get_info efi_compat_get_info
#define xenpf_efi_info compat_pf_efi_info
+#define efi_runtime_call efi_compat_runtime_call
+#define xenpf_efi_runtime_call compat_pf_efi_runtime_call
+
+#define xenpf_efi_guid compat_pf_efi_guid
+#define xenpf_efi_time compat_pf_efi_time
+
#define COMPAT
#undef DEFINE_XEN_GUEST_HANDLE
#define DEFINE_XEN_GUEST_HANDLE DEFINE_COMPAT_HANDLE
+#undef XEN_GUEST_HANDLE
+#define XEN_GUEST_HANDLE COMPAT_HANDLE
#undef guest_handle_okay
#define guest_handle_okay compat_handle_okay
#undef guest_handle_cast
#define guest_handle_cast compat_handle_cast
+#undef __copy_from_guest
+#define __copy_from_guest __copy_from_compat
+#undef copy_from_guest_offset
+#define copy_from_guest_offset copy_from_compat_offset
+#undef copy_to_guest
+#define copy_to_guest copy_to_compat
#undef __copy_to_guest_offset
#define __copy_to_guest_offset __copy_to_compat_offset
#include "runtime.c"
#include <efi/efidevp.h>
#include <efi/efiapi.h>
#include <xen/efi.h>
+#include <xen/spinlock.h>
+#include <asm/page.h>
extern unsigned int efi_num_ct;
extern EFI_CONFIGURATION_TABLE *efi_ct;
extern UINTN efi_memmap_size, efi_mdesc_size;
extern void *efi_memmap;
+
+extern l4_pgentry_t *efi_l4_pgtable;
+
+unsigned long efi_rs_enter(void);
+void efi_rs_leave(unsigned long);
#include <xen/cache.h>
#include <xen/errno.h>
#include <xen/guest_access.h>
+#include <xen/time.h>
DEFINE_XEN_GUEST_HANDLE(CHAR16);
const CHAR16 *__read_mostly efi_fw_vendor;
EFI_RUNTIME_SERVICES *__read_mostly efi_rs;
+static DEFINE_SPINLOCK(efi_rs_lock);
UINTN __read_mostly efi_memmap_size;
UINTN __read_mostly efi_mdesc_size;
.smbios = EFI_INVALID_TABLE_ADDR,
};
+l4_pgentry_t *__read_mostly efi_l4_pgtable;
+
+unsigned long efi_rs_enter(void)
+{
+ unsigned long cr3 = read_cr3();
+
+ spin_lock(&efi_rs_lock);
+
+ /* prevent fixup_page_fault() from doing anything */
+ irq_enter();
+
+ write_cr3(virt_to_maddr(efi_l4_pgtable));
+
+ return cr3;
+}
+
+void efi_rs_leave(unsigned long cr3)
+{
+ write_cr3(cr3);
+ irq_exit();
+ spin_unlock(&efi_rs_lock);
+}
+
+unsigned long efi_get_time(void)
+{
+ EFI_TIME time;
+ EFI_STATUS status;
+ unsigned long cr3 = efi_rs_enter();
+
+ status = efi_rs->GetTime(&time, NULL);
+ efi_rs_leave(cr3);
+
+ if ( EFI_ERROR(status) )
+ return 0;
+
+ return mktime(time.Year, time.Month, time.Day,
+ time.Hour, time.Minute, time.Second);
+}
+
+void efi_halt_system(void)
+{
+ EFI_STATUS status;
+ unsigned long cr3 = efi_rs_enter();
+
+ status = efi_rs->ResetSystem(EfiResetShutdown, EFI_SUCCESS, 0, NULL);
+ efi_rs_leave(cr3);
+
+ printk(XENLOG_WARNING "EFI: could not halt system (%#lx)\n", status);
+}
+
+void efi_reset_system(bool_t warm)
+{
+ EFI_STATUS status;
+ unsigned long cr3 = efi_rs_enter();
+
+ status = efi_rs->ResetSystem(warm ? EfiResetWarm : EfiResetCold,
+ EFI_SUCCESS, 0, NULL);
+ efi_rs_leave(cr3);
+
+ printk(XENLOG_WARNING "EFI: could not reset system (%#lx)\n", status);
+}
+
#endif
int efi_get_info(uint32_t idx, union xenpf_efi_info *info)
return 0;
}
+
+static long gwstrlen(XEN_GUEST_HANDLE(CHAR16) str)
+{
+ unsigned long len;
+
+ for ( len = 0; ; ++len )
+ {
+ CHAR16 c;
+
+ if ( copy_from_guest_offset(&c, str, len, 1) )
+ return -EFAULT;
+ if ( !c )
+ break;
+ }
+
+ return len;
+}
+
+static inline EFI_TIME *cast_time(struct xenpf_efi_time *time)
+{
+#define chk_fld(F, f) \
+ BUILD_BUG_ON(sizeof(cast_time(NULL)->F) != sizeof(time->f) || \
+ offsetof(EFI_TIME, F) != offsetof(struct xenpf_efi_time, f))
+ chk_fld(Year, year);
+ chk_fld(Month, month);
+ chk_fld(Day, day);
+ chk_fld(Hour, hour);
+ chk_fld(Minute, min);
+ chk_fld(Second, sec);
+ chk_fld(Nanosecond, ns);
+ chk_fld(TimeZone, tz);
+ chk_fld(Daylight, daylight);
+#undef chk_fld
+ return (void *)time;
+}
+
+static inline EFI_GUID *cast_guid(struct xenpf_efi_guid *guid)
+{
+#define chk_fld(n) \
+ BUILD_BUG_ON(sizeof(cast_guid(NULL)->Data##n) != sizeof(guid->data##n) || \
+ offsetof(EFI_GUID, Data##n) != \
+ offsetof(struct xenpf_efi_guid, data##n))
+ chk_fld(1);
+ chk_fld(2);
+ chk_fld(3);
+ chk_fld(4);
+#undef chk_fld
+ return (void *)guid;
+}
+
+int efi_runtime_call(struct xenpf_efi_runtime_call *op)
+{
+ unsigned long cr3;
+ EFI_STATUS status = EFI_NOT_STARTED;
+ int rc = 0;
+
+ switch ( op->function )
+ {
+ case XEN_EFI_get_time:
+ {
+ EFI_TIME_CAPABILITIES caps;
+
+ if ( op->misc )
+ return -EINVAL;
+
+ cr3 = efi_rs_enter();
+ status = efi_rs->GetTime(cast_time(&op->u.get_time.time), &caps);
+ efi_rs_leave(cr3);
+
+ if ( !EFI_ERROR(status) )
+ {
+ op->u.get_time.resolution = caps.Resolution;
+ op->u.get_time.accuracy = caps.Accuracy;
+ if ( caps.SetsToZero )
+ op->misc = XEN_EFI_GET_TIME_SET_CLEARS_NS;
+ }
+ }
+ break;
+
+ case XEN_EFI_set_time:
+ if ( op->misc )
+ return -EINVAL;
+
+ cr3 = efi_rs_enter();
+ status = efi_rs->SetTime(cast_time(&op->u.set_time));
+ efi_rs_leave(cr3);
+ break;
+
+ case XEN_EFI_get_wakeup_time:
+ {
+ BOOLEAN enabled, pending;
+
+ if ( op->misc )
+ return -EINVAL;
+
+ cr3 = efi_rs_enter();
+ status = efi_rs->GetWakeupTime(&enabled, &pending,
+ cast_time(&op->u.get_wakeup_time));
+ efi_rs_leave(cr3);
+
+ if ( !EFI_ERROR(status) )
+ {
+ if ( enabled )
+ op->misc |= XEN_EFI_GET_WAKEUP_TIME_ENABLED;
+ if ( pending )
+ op->misc |= XEN_EFI_GET_WAKEUP_TIME_PENDING;
+ }
+ }
+ break;
+
+ case XEN_EFI_set_wakeup_time:
+ if ( op->misc & ~(XEN_EFI_SET_WAKEUP_TIME_ENABLE |
+ XEN_EFI_SET_WAKEUP_TIME_ENABLE_ONLY) )
+ return -EINVAL;
+
+ cr3 = efi_rs_enter();
+ status = efi_rs->SetWakeupTime(!!(op->misc &
+ XEN_EFI_SET_WAKEUP_TIME_ENABLE),
+ (op->misc &
+ XEN_EFI_SET_WAKEUP_TIME_ENABLE_ONLY) ?
+ NULL :
+ cast_time(&op->u.set_wakeup_time));
+ efi_rs_leave(cr3);
+
+ op->misc = 0;
+ break;
+
+ case XEN_EFI_get_next_high_monotonic_count:
+ if ( op->misc )
+ return -EINVAL;
+
+ cr3 = efi_rs_enter();
+ status = efi_rs->GetNextHighMonotonicCount(&op->misc);
+ efi_rs_leave(cr3);
+ break;
+
+ case XEN_EFI_get_variable:
+ {
+ CHAR16 *name;
+ long len;
+ unsigned char *data;
+ UINTN size;
+
+ if ( op->misc )
+ return -EINVAL;
+
+ len = gwstrlen(guest_handle_cast(op->u.get_variable.name, CHAR16));
+ if ( len < 0 )
+ return len;
+ name = xmalloc_array(CHAR16, ++len);
+ if ( !name )
+ return -ENOMEM;
+ __copy_from_guest(name, op->u.get_variable.name, len);
+
+ size = op->u.get_variable.size;
+ if ( size )
+ {
+ data = xmalloc_bytes(size);
+ if ( !data )
+ {
+ xfree(name);
+ return -ENOMEM;
+ }
+ }
+ else
+ data = NULL;
+
+ cr3 = efi_rs_enter();
+ status = efi_rs->GetVariable(
+ name, cast_guid(&op->u.get_variable.vendor_guid),
+ &op->misc, &size, data);
+ efi_rs_leave(cr3);
+
+ if ( !EFI_ERROR(status) &&
+ copy_to_guest(op->u.get_variable.data, data, size) )
+ rc = -EFAULT;
+ op->u.get_variable.size = size;
+
+ xfree(data);
+ xfree(name);
+ }
+ break;
+
+ case XEN_EFI_set_variable:
+ {
+ CHAR16 *name;
+ long len;
+ unsigned char *data;
+
+ if ( op->misc )
+ return -EINVAL;
+
+ len = gwstrlen(guest_handle_cast(op->u.set_variable.name, CHAR16));
+ if ( len < 0 )
+ return len;
+ name = xmalloc_array(CHAR16, ++len);
+ if ( !name )
+ return -ENOMEM;
+ __copy_from_guest(name, op->u.set_variable.name, len);
+
+ data = xmalloc_bytes(op->u.set_variable.size);
+ if ( !data )
+ rc = -ENOMEM;
+ else if ( copy_from_guest(data, op->u.set_variable.data,
+ op->u.set_variable.size) )
+ rc = -EFAULT;
+ else
+ {
+ cr3 = efi_rs_enter();
+ status = efi_rs->SetVariable(
+ name, cast_guid(&op->u.set_variable.vendor_guid),
+ op->misc, op->u.set_variable.size, data);
+ efi_rs_leave(cr3);
+ }
+
+ xfree(data);
+ xfree(name);
+ }
+ break;
+
+ case XEN_EFI_get_next_variable_name:
+ {
+ union {
+ CHAR16 *str;
+ unsigned char *raw;
+ } name;
+ UINTN size;
+
+ if ( op->misc )
+ return -EINVAL;
+
+ size = op->u.get_next_variable_name.size;
+ name.raw = xmalloc_bytes(size);
+ if ( !name.raw )
+ return -ENOMEM;
+ copy_from_guest(name.raw, op->u.get_next_variable_name.name, size);
+
+ cr3 = efi_rs_enter();
+ status = efi_rs->GetNextVariableName(
+ &size, name.str,
+ cast_guid(&op->u.get_next_variable_name.vendor_guid));
+ efi_rs_leave(cr3);
+
+ if ( !EFI_ERROR(status) &&
+ copy_to_guest(op->u.get_next_variable_name.name, name.raw, size) )
+ rc = -EFAULT;
+ op->u.get_next_variable_name.size = size;
+
+ xfree(name.raw);
+ }
+ break;
+
+ default:
+ return -ENOSYS;
+ }
+
+#ifndef COMPAT
+ op->status = status;
+#else
+ op->status = (status & 0x3fffffff) | (status >> 62);
+#endif
+
+ return rc;
+}
#include <xen/efi.h>
#include <xen/errno.h>
#include <xen/init.h>
+#include <asm/bug.h>
#ifndef efi_enabled
const bool_t efi_enabled = 0;
void __init efi_init_memory(void) { }
+unsigned long efi_get_time(void)
+{
+ BUG();
+ return 0;
+}
+
+void efi_halt_system(void) { }
+void efi_reset_system(bool_t warm) { }
+
int efi_get_info(uint32_t idx, union xenpf_efi_info *info)
{
return -ENOSYS;
int efi_compat_get_info(uint32_t idx, union compat_pf_efi_info *)
__attribute__((__alias__("efi_get_info")));
+
+int efi_runtime_call(struct xenpf_efi_runtime_call *op)
+{
+ return -ENOSYS;
+}
+
+int efi_compat_runtime_call(struct compat_pf_efi_runtime_call *)
+ __attribute__((__alias__("efi_runtime_call")));
}
break;
+ case XENPF_efi_runtime_call:
+ ret = xsm_efi_runtime_call();
+ if ( ret )
+ break;
+
+ ret = efi_runtime_call(&op->u.efi_runtime_call);
+ if ( ret == 0 &&
+ copy_field_to_guest(u_xenpf_op, op, u.efi_runtime_call) )
+ ret = -EFAULT;
+ break;
+
case XENPF_enter_acpi_sleep:
ret = xsm_acpi_sleep();
if ( ret )
#include <xen/console.h>
#include <xen/shutdown.h>
#include <xen/acpi.h>
+#include <xen/efi.h>
#include <asm/msr.h>
#include <asm/regs.h>
#include <asm/mc146818rtc.h>
watchdog_disable();
console_start_sync();
local_irq_enable();
+ efi_halt_system();
smp_call_function(__machine_halt, NULL, 0);
__machine_halt(NULL);
}
if ( tboot_in_measured_env() )
tboot_shutdown(TB_SHUTDOWN_REBOOT);
+ efi_reset_system(reboot_mode != 0);
+
/* Rebooting needs to touch the page at absolute address 0. */
*((unsigned short *)__va(0x472)) = reboot_mode;
#include <xen/smp.h>
#include <xen/irq.h>
#include <xen/softirq.h>
+#include <xen/efi.h>
#include <xen/cpuidle.h>
#include <xen/symbols.h>
#include <xen/keyhandler.h>
unsigned long res, flags;
int i;
+ if ( efi_enabled )
+ {
+ res = efi_get_time();
+ if ( res )
+ return res;
+ }
+
spin_lock_irqsave(&rtc_lock, flags);
/* read RTC exactly on falling edge of update flag */
#define do_platform_op(x) compat_platform_op(_##x)
#define efi_get_info efi_compat_get_info
+#define efi_runtime_call(x) efi_compat_runtime_call(x)
#define xen_processor_px compat_processor_px
#define xen_processor_px_t compat_processor_px_t
typedef struct xenpf_platform_quirk xenpf_platform_quirk_t;
DEFINE_XEN_GUEST_HANDLE(xenpf_platform_quirk_t);
+#define XENPF_efi_runtime_call 49
+#define XEN_EFI_get_time 1
+#define XEN_EFI_set_time 2
+#define XEN_EFI_get_wakeup_time 3
+#define XEN_EFI_set_wakeup_time 4
+#define XEN_EFI_get_next_high_monotonic_count 5
+#define XEN_EFI_get_variable 6
+#define XEN_EFI_set_variable 7
+#define XEN_EFI_get_next_variable_name 8
+struct xenpf_efi_runtime_call {
+ uint32_t function;
+ /*
+ * This field is generally used for per sub-function flags (defined
+ * below), except for the XEN_EFI_get_next_high_monotonic_count case,
+ * where it holds the single returned value.
+ */
+ uint32_t misc;
+ unsigned long status;
+ union {
+#define XEN_EFI_GET_TIME_SET_CLEARS_NS 0x00000001
+ struct {
+ struct xenpf_efi_time {
+ uint16_t year;
+ uint8_t month;
+ uint8_t day;
+ uint8_t hour;
+ uint8_t min;
+ uint8_t sec;
+ uint32_t ns;
+ int16_t tz;
+ uint8_t daylight;
+ } time;
+ uint32_t resolution;
+ uint32_t accuracy;
+ } get_time;
+
+ struct xenpf_efi_time set_time;
+
+#define XEN_EFI_GET_WAKEUP_TIME_ENABLED 0x00000001
+#define XEN_EFI_GET_WAKEUP_TIME_PENDING 0x00000002
+ struct xenpf_efi_time get_wakeup_time;
+
+#define XEN_EFI_SET_WAKEUP_TIME_ENABLE 0x00000001
+#define XEN_EFI_SET_WAKEUP_TIME_ENABLE_ONLY 0x00000002
+ struct xenpf_efi_time set_wakeup_time;
+
+#define XEN_EFI_VARIABLE_NON_VOLATILE 0x00000001
+#define XEN_EFI_VARIABLE_BOOTSERVICE_ACCESS 0x00000002
+#define XEN_EFI_VARIABLE_RUNTIME_ACCESS 0x00000004
+ struct {
+ XEN_GUEST_HANDLE(void) name; /* UCS-2/UTF-16 string */
+ unsigned long size;
+ XEN_GUEST_HANDLE(void) data;
+ struct xenpf_efi_guid {
+ uint32_t data1;
+ uint16_t data2;
+ uint16_t data3;
+ uint8_t data4[8];
+ } vendor_guid;
+ } get_variable, set_variable;
+
+ struct {
+ unsigned long size;
+ XEN_GUEST_HANDLE(void) name; /* UCS-2/UTF-16 string */
+ struct xenpf_efi_guid vendor_guid;
+ } get_next_variable_name;
+ } u;
+};
+typedef struct xenpf_efi_runtime_call xenpf_efi_runtime_call_t;
+DEFINE_XEN_GUEST_HANDLE(xenpf_efi_runtime_call_t);
+
#define XENPF_firmware_info 50
#define XEN_FW_DISK_INFO 1 /* from int 13 AH=08/41/48 */
#define XEN_FW_DISK_MBR_SIGNATURE 2 /* from MBR offset 0x1b8 */
struct xenpf_read_memtype read_memtype;
struct xenpf_microcode_update microcode;
struct xenpf_platform_quirk platform_quirk;
+ struct xenpf_efi_runtime_call efi_runtime_call;
struct xenpf_firmware_info firmware_info;
struct xenpf_enter_acpi_sleep enter_acpi_sleep;
struct xenpf_change_freq change_freq;
union xenpf_efi_info;
union compat_pf_efi_info;
+struct xenpf_efi_runtime_call;
+struct compat_pf_efi_runtime_call;
+
void efi_init_memory(void);
+unsigned long efi_get_time(void);
+void efi_halt_system(void);
+void efi_reset_system(bool_t warm);
#ifndef COMPAT
int efi_get_info(uint32_t idx, union xenpf_efi_info *);
+int efi_runtime_call(struct xenpf_efi_runtime_call *);
#endif
int efi_compat_get_info(uint32_t idx, union compat_pf_efi_info *);
+int efi_compat_runtime_call(struct compat_pf_efi_runtime_call *);
#endif /* __XEN_EFI_H__ */
int (*physinfo) (void);
int (*platform_quirk) (uint32_t);
int (*firmware_info) (void);
+ int (*efi_runtime_call) (void);
int (*acpi_sleep) (void);
int (*change_freq) (void);
int (*getidletime) (void);
return xsm_call(firmware_info());
}
+static inline int xsm_efi_runtime_call (void)
+{
+ return xsm_call(efi_runtime_call());
+}
+
static inline int xsm_acpi_sleep (void)
{
return xsm_call(acpi_sleep());